#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/wait.h>
//#include <readline/readline.h>
#include <string.h>
#include "shelllib.h"
#include "argvs.h"
#include "Debug.h"
#include "head.h"
#include "restart.h"

// Only for compiler
static int do_exit(int argc,char *argv[]);
static int do_cd(int argc,char *argv[]);
static int do_echo(int argc,char *argv[]);
static int do_export(int argc,char *argv[]);
static int do_history(int argc,char *argv[]);
static int do_help(int argc,char *argv[]);
static int do_pwd(int argc,char *argv[]);
static int execute_internal_function(int argc,char *argv[]);
static int execute_external_function(int argc,char *argv[]);
static int search_for_key(const char *key);

INTERNAL_FUNTION Cmd_Funtion_table[] = {
	{
		"exit",
		"exit the system"
		 "\nUsage:exit [n]\n",	
		do_exit
	},
	{
		"cd",
		"cd to a directory"
		  "\nUsage:cd [-L|-P] [dir]\n",	
		do_cd
	},
	{
		"echo",
		"echo a string"
	      "\nUsage:echo [-neE] [arg ...]\n",
		do_echo
	},
	{
		"export",
		"define a symbol variable"
		 "\nUsage:export [-nf] [name[=value] ...] or export -p\n",
		do_export
	},
	{
		"history",
		"list the history command"
		  "\nUsage:history [-c] [-d offset] [n] or"
		  "\thistory -awrn [filename] or history -ps arg [arg...]\n",	
		do_history
	},
	{
		"help",
		"list the command infomation"
		 "\nUsage: help [-s] [pattern ...]\n",
		do_help
	},
	{
		"pwd",
		"pwd - print name of current/working directory"
		  "\nUsage pwd [-LP]",
		do_pwd
	},


};
/*
char *get_input()
{
	char *line = NULL;
	char prompt[100] = "\n";
	while(1){

//		rl_set_prompt(prompt);
		if (!(line = readline(prompt))){
				break;
		}
		printf("%s\n",line);
	}

	return line;
}
*/

void printf_prompt(const char *prompt)
{
	printf("%s",prompt);
}

int check_exit(const char *cmdlist)
{
	return (strcmp(cmdlist,EXIT) == 0);

}

int get_input(char *buf)
{
	int len = 0;
	int ch;

	ch = getchar();
	while(len < CMD_LEN_SIZE - 1 && ch != '\n'){
		buf[len++] = ch;
		ch = getchar();
	}
	
	if (len == CMD_LEN_SIZE - 1){
		printf("command is too long\n");
	}

	buf[len] = '\0';

	return len;
}

static int do_simple_cmd(char *cmd,int *prefd,int *postfd)
{
	int argc;
	char **argvs;
	if ((argc = makeargv(cmd," ",&argvs)) == -1){
		perror("makeargv");
		return -1;
	}

	/*
	for (i = 0;argvs[i] != NULL;i++){
	}
*/
	if (execute_internal_function(argc,argvs) == UNEXIST){

		if (execute_external_function(argc,argvs)){
			printf("Not found the command: %s\n",argvs[0]);
		}
	}

	freemakeargv(argvs);

	return 0;
}

static void do_pipe_cmd(char *pipe_cmd)
{
	int i =0;
	int j = 0;
	int prepipe = 0;
	int prefd[2],postfd[2];
	char tmp;


	while(pipe_cmd[i]){
		if (pipe_cmd[i] == '|'){
			tmp = pipe_cmd[i];
			pipe_cmd[i] = '\0';
			pipe(postfd);  // create the post pipe
			if (prepipe){
				do_simple_cmd(pipe_cmd+j,prefd,postfd);
			}
			else{
				do_simple_cmd(pipe_cmd+j,NULL,postfd);
			}
			pipe_cmd[i] = tmp;
			prepipe = 1;
			prefd[0] = postfd[0];
			prefd[1] = postfd[1];
			j = ++i;
		}
		else{
			i++;
		}
	}
	do_simple_cmd(pipe_cmd+j,NULL,NULL);

}

void do_list_cmd(char *cmdlist)
{
	int i = 0;
	int j = 0;
	char tmp;
	
	while(cmdlist[i]){
		if (cmdlist[i] ==';'){ // execute ";"
			tmp = cmdlist[i];
			cmdlist[i] = '\0';
			do_pipe_cmd(cmdlist + j);
			cmdlist[i] = tmp;
			j = ++i;
		}
		else
			i++;
	}

	do_pipe_cmd(cmdlist + j);

}



static int do_exit(int argc,char *argv[])
{
	printf("unsupport %s\n",argv[0]);
	return UNSUPPORTED;
}

static int do_cd(int argc,char *argv[])
{
	if (argc == 1){
		if (chdir(".") == -1){
			perror(".");
			return errno;
		}
		return 0;
	}

	if (chdir(argv[1]) == -1){
		perror(argv[1]);
		return errno;
	}
	return 0;
}

static int do_echo(int argc,char *argv[])
{
	int i;

	for (i = 1;i < argc;i++){
		printf("%s ",argv[i]);
	}
	printf("\n");
	return SUCCESS;
}

static int do_export(int argc,char *argv[])
{
	printf("unsupport %s\n",argv[0]);
	return UNSUPPORTED;
}

static int do_history(int argc,char *argv[])
{
	printf("unsupport %s\n",argv[0]);
	return 0;
}

static int do_help(int argc,char *argv[])
{
	int i;

	if (argc == 1 || (argc == 2 && strcmp(argv[1],"-s") == 0)){
		for (i = 0;i < internal_fun_len;i++){
			printf("%s: %s",Cmd_Funtion_table[i].cmd,Cmd_Funtion_table[i].help_info);
		}
	}
	else if (argc > 2 && strcmp(argv[1],"-s") == 0){
		int locate,i;
		for (i = 2;i < argc;i++){
			locate = search_for_key(argv[i]);
			if (locate == -1){
				printf("help: no help topics match \'%s\'\n",argv[i]);
			}
			else{
				printf("%s: %s",Cmd_Funtion_table[locate].cmd,
						Cmd_Funtion_table[locate].help_info);
				
			}
		}
	}

	else if (argc == 2){
		printf("help: no help topics match \'%s\', Try \'help -s %s\'\n",
				argv[1],argv[1]);
	}
	return 0;
}

static int do_pwd(int argc,char *argv[])
{
	char *path;
	int size = DIR_LEN;

    path = (char *)malloc(size * sizeof(char));
    if (path == NULL){
		perror("malloc");
        return errno;
	}

	while(1){
		char *newbase;
	     if (getcwd(path,size) != NULL){
			printf("%s\n",path);
		    break;
	     }

	     newbase = (char *)realloc(path,size * 2);
         if (newbase == NULL){
			 perror("realloc");
			 free(path);
			 return errno;
		 }
	     path = newbase;
	     size *= 2;
	}
	free(path);

    return 0;
}

static int search_for_key(const char *key)
{
	int i;
	int locate = -1;
	char *pBuf = NULL;

	pBuf = (char *) malloc(strlen(key) + 1);
	if (pBuf == NULL){
		perror("malloc");
		return locate;
	}

	// cp the key to pBuf and turn to lower.
	strcpy(pBuf,key);
	TurnTolower(pBuf);

	// search with pBuf.
	for (i = 0;i < internal_fun_len;i++){
		if (strcmp(Cmd_Funtion_table[i].cmd,pBuf) == 0){
			locate = i;
			break;
		}
	}

	free(pBuf);

	return locate;
}

static int execv_for_key(int argc,const char *key,char *argv[])
{
	int location;

	location = search_for_key(key);
	if (location == -1){
		return UNEXIST;
	}
	return Cmd_Funtion_table[location].function(argc,argv);
}

static int execute_internal_function(int argc,char *argv[])
{
	return execv_for_key(argc,argv[0],argv);
}

  
static int execute_external_function(int argc,char *argv[])
{
	pid_t childpid;

	childpid = fork();
	if (childpid == -1){
		perror("Failed to fork");
		return errno;
	}

	if (childpid == 0){		// child code
		execvp(argv[0],argv);
		return errno;
	}

	if (childpid != t_wait(NULL)){	// parent code
		return errno;
	}
	return 0;
}





